package com.wolfking.sharding.seq;


import java.util.concurrent.atomic.AtomicLong;

public abstract class AbstractSeqService implements SeqService {
    /**
     * 当前最小
     */
    private final AtomicLong minAtom = new AtomicLong(-1);

    /**
     * 当前最大
     */
    private final AtomicLong maxAtom = new AtomicLong(-1);

    /**
     * 从数据库加载的锁
     */
    private final Object lock = new Object();


    /**
     * 获取下一个ID
     *
     * @return
     */
    @Override
    public Long getNextId() {
        while (true) {
            //每次通过load判断是否需要加载，先修改min，再修改max，
            //需要load的时候都能走到load并且阻塞线程
            load();
            //下面的两个顺序不可调换，不然会出意想不到的问题
            long max = maxAtom.get();
            long l = minAtom.incrementAndGet();
            if (l <= max) {
                return l;
            }
        }
    }


    private void load() {
        //使用double check 从数据库捞取
        if (minAtom.get() < 0 || minAtom.get() >= maxAtom.get()) {
            synchronized (lock) {
                if (minAtom.get() < 0 || minAtom.get() >= maxAtom.get()) {
                    int step = getStepSize();
                    long loadNextId = loadNextId(step);
                    //循环cas设置min，再设置max，min很多线程都在修改，使用循环
                    while (!minAtom.compareAndSet(minAtom.get(), loadNextId - step)) ;
                    // max同一时间只有一个线程在修改
                    maxAtom.compareAndSet(maxAtom.get(), loadNextId);
                }
            }
        }

    }

    protected abstract long loadNextId(Integer step);

    protected int getStepSize(){
        return 1;
    }
}
